/**
 * @brief FreeRTOS应用示例
 * @details
 * @author 王炸物联网https://blog.csdn.net/wangyx1234?spm=1000.2115.3001.5343
 * @date 2022
 * @version 1.0
 * @copyright 版权所有，禁止用于商业用途
 */
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/ringbuf.h"

#include "esp_system.h"
#include "esp_spi_flash.h"
#include "esp_log.h"

#include "driver/gpio.h"
#include "esp_rom_sys.h"

#define TASK_TAG1      "TASK1"
#define TASK_TAG2      "TASK2"
#define TASK_TAG3      "TASK3"
#define TAG            "custom test"
//Definitions used in multiple test cases
#define TIMEOUT_TICKS               10
#define ITEM_HDR_SIZE               8
#define SMALL_ITEM_SIZE             8
#define LARGE_ITEM_SIZE             (2 * SMALL_ITEM_SIZE)
#define BUFFER_SIZE                 96     //4Byte aligned size

#define TEST_ASSERT_MESSAGE(ret, printf_str)        if(ret) {} else {ESP_LOGE(TAG, printf_str);}

TaskHandle_t task1; // 任务1结构对象
TaskHandle_t task2; // 任务1结构对象   

static int task1_flag;
static int task2_flag;

static RingbufHandle_t custom_ring_buffer;
static int no_of_items;

static const uint8_t small_item[SMALL_ITEM_SIZE] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
static const uint8_t large_item[LARGE_ITEM_SIZE] = { 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F,
                                                     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17};

static void send_item_and_check(RingbufHandle_t handle, const uint8_t *item,  size_t item_size, TickType_t ticks_to_wait)
{
    BaseType_t ret;

    ret = xRingbufferSend(handle, (void *)item, item_size, ticks_to_wait);
    TEST_ASSERT_MESSAGE(ret == pdTRUE, "send fail");
}

static void receive_check_and_return_item_no_split(RingbufHandle_t handle, const uint8_t *expected_data, size_t expected_size, TickType_t ticks_to_wait)
{
    //Receive item from no-split buffer
    size_t item_size;
    uint8_t *item;

    item = (uint8_t *)xRingbufferReceive(handle, &item_size, ticks_to_wait);

    TEST_ASSERT_MESSAGE(item != NULL, "Failed to receive item");
    TEST_ASSERT_MESSAGE(item_size == expected_size, "Item size is incorrect");
    //Check data of received item
    for (int i = 0; i < item_size; i++) {
        TEST_ASSERT_MESSAGE(item[i] == expected_data[i], "Item data is invalid");
    }
    //Return item

    vRingbufferReturnItem(handle, (void *)item);
}

/**
 * 任务的运行代码
 * @param param 任务初始运行参数
 */
static void task1_process(void *arg)
{
    while (1) {
        //Test sending items
        if (task1_flag < no_of_items) {
            send_item_and_check(custom_ring_buffer, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS);
        } else {
            //Send large item that causes wrap around
            send_item_and_check(custom_ring_buffer, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS);
        }

        task1_flag++;
        
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

/**
 * 任务的运行代码
 * @param param 任务初始运行参数
 */
static void task2_process(void *arg)
{
    static const char *TASK2_TAG = TASK_TAG2;
    while (1) {
       printf("%s:flag=%d\n", TASK2_TAG, task2_flag);
        //Test receiving items
        if (task2_flag < no_of_items) {
            receive_check_and_return_item_no_split(custom_ring_buffer, small_item, SMALL_ITEM_SIZE, TIMEOUT_TICKS);
        } else {
            //Receive wrapped item
            receive_check_and_return_item_no_split(custom_ring_buffer, large_item, LARGE_ITEM_SIZE, TIMEOUT_TICKS);
        }
        
        UBaseType_t free_pos, read_pos, write_pos, acquire_pos, num_of_items_pos;
        vRingbufferGetInfo(custom_ring_buffer, &free_pos, &read_pos, &write_pos, &acquire_pos, &num_of_items_pos);
        printf("%s:free_pos=0x%08x, read_pos=0x%08x, write_pos=0x%08x, acquire_pos=0x%08x, num_of_items_pos=0x%08u\n",
        TASK2_TAG, 
        free_pos,
        read_pos,
        write_pos,
        acquire_pos,
        num_of_items_pos);

        task2_flag++;
        
        vTaskDelay(pdMS_TO_TICKS(500));
    }
}

static void init(void) {
    printf("Hello world!\n");

    /* Print chip information */
    esp_chip_info_t chip_info;
    esp_chip_info(&chip_info);
    printf("This is %s chip with %d CPU core(s), WiFi%s%s, ",
            CONFIG_IDF_TARGET,
            chip_info.cores,
            (chip_info.features & CHIP_FEATURE_BT) ? "/BT" : "",
            (chip_info.features & CHIP_FEATURE_BLE) ? "/BLE" : "");

    printf("Minimum free heap size: %d bytes\n", esp_get_minimum_free_heap_size());

   custom_ring_buffer = xRingbufferCreate(BUFFER_SIZE, RINGBUF_TYPE_NOSPLIT);

    if (custom_ring_buffer == NULL) {
        printf("created fail\n");
    }
    size_t max_item_size = xRingbufferGetMaxItemSize(custom_ring_buffer);

    //Calculate number of items to send. Aim to almost fill buffer to setup for wrap around
    no_of_items = (BUFFER_SIZE - (ITEM_HDR_SIZE + SMALL_ITEM_SIZE)) / (ITEM_HDR_SIZE + SMALL_ITEM_SIZE);
    printf("ring buffer Max item size is %d, num of items is %d\n", max_item_size, no_of_items);
}

void app_main(void)
{
    init();
    /*
    static BaseType_t xTaskCreate(TaskFunction_t pvTaskCode, const char *constpcName, const uint32_t usStackDepth, 
    void *constpvParameters, UBaseType_t uxPriority, TaskHandle_t *constpxCreatedTask)*/
    xTaskCreate(&task1_process, "task1", 1024*2, (void *)"1", configMAX_PRIORITIES-1, &task1); // configMAX_PRIORITIES = 25
    xTaskCreate(&task2_process, "task2", 1024*2, (void *)"2", configMAX_PRIORITIES-2, &task2); // configMAX_PRIORITIES = 25
}

